﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "UiNotificationManager.h"

#include "QtNotificationBar.h"
#include "QtNotificationMessageBox.h"
#include "QtNotificationHistoryPanel.h"
#include "IUiManager.h"
#include "IApplicationWindow.h"

#include "INotificationEventHandler.h"
#include "INotificationEventArgs.h"
#include "INotificationEvent.h"
#include "INotificationManager.h"
#include "INotification.h"

#include "GnufQtNew.h"
#include "GnufQtObjectPtr.h"

#include <QWidget>
#include <QResizeEvent>
#include <QtCore/QPointer>

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;


namespace
{
    const double s_opacity0 = 0.2;
    const double s_opacity1 = 0.4;
    const double s_opacity2 = 0.65;
    const int s_gap = 4;
}


namespace gcmp
{
    class UiNotificationManagerCreator
    {
    public:
        UiNotificationManager* Get() { return &m_uiNotificationManager; }

    private:
        UiNotificationManager m_uiNotificationManager;
    };
    UiNotificationManagerCreator s_uiNotificationManagerCreator;


    class UiNotificationManagerImpl : public QObject, public INotificationEventHandler
    {
    protected:
        virtual bool eventFilter(QObject *watched, QEvent *event) override
        {
            QWidget* pWidget = dynamic_cast<QWidget*>(watched);
            if (m_pParentWidget && pWidget)
            {
                if (m_pParentWidget == pWidget &&
                    (dynamic_cast<QMoveEvent*>(event) || dynamic_cast<QResizeEvent*>(event)))
                {
                    MoveNotification();
                }
                else if(dynamic_cast<QMoveEvent*>(event))
                {
                    IUiManager* pUiMgr = IUiManager::Get();
                    DBG_WARN_AND_RETURN_FALSE_UNLESS(pUiMgr, L"pUiMgr为空！",L"GDMPLab",L"2024-03-30");

                    IApplicationWindow* pMainWnd = pUiMgr->GetApplicationWindow();
                    DBG_WARN_AND_RETURN_FALSE_UNLESS(pMainWnd, L"pMainWnd为空！",L"GDMPLab",L"2024-03-30");

                    if (pWidget->winId() == pMainWnd->GetApplicationWindowId() && dynamic_cast<QMoveEvent*>(event))
                    {
                        MoveNotification();
                    }
                }
            }
            return false;
        }

    public:
        UiNotificationManagerImpl() : m_pParentWidget(nullptr)
        {
            gcmp::INotificationManager::Get()->GetNotificationEvent()->Add(this);
        }

        ~UiNotificationManagerImpl()
        {
            // Qt子控件由父控件释放
            m_notifications[0].releaseOwnership();
            m_notifications[1].releaseOwnership();
            m_notifications[2].releaseOwnership();
            m_notificationBar.releaseOwnership();
            m_notificationHistoryPanel.releaseOwnership();

            if (m_pParentWidget)
            {
                m_pParentWidget->removeEventFilter(this);

                // 为什么主窗口Move不引起MdiArea的移动事件 ？？？？
                IUiManager* pUiMgr = IUiManager::Get();
                DBG_WARN_AND_RETURN_VOID_UNLESS(pUiMgr, L"pUiMgr为空！",L"GDMPLab",L"2024-03-30");
                IApplicationWindow* pAppWnd = pUiMgr->GetApplicationWindow();
                DBG_WARN_AND_RETURN_VOID_UNLESS(pAppWnd, L"pAppWnd为空！",L"GDMPLab",L"2024-03-30");

                QWidget* pMainWindowWidget = QWidget::find((WId)pAppWnd->GetApplicationWindowId());
                DBG_WARN_AND_RETURN_VOID_UNLESS(pMainWindowWidget, L"pMainWindowWidget为空！",L"GDMPLab",L"2024-03-30");
                pMainWindowWidget->removeEventFilter(this);
            }
        }

        void On(INotificationEventArgs* pArgs) override
        {
            if (!pArgs)
                return;
            const INotification* pNotification = pArgs->GetNotification();
            if (!pNotification)
                return;

            IUiManager* pUiMgr = IUiManager::Get();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pUiMgr, L"pUiMgr为空！",L"GDMPLab",L"2024-03-30");
            IApplicationWindow* pAppWnd = pUiMgr->GetApplicationWindow();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pAppWnd, L"pAppWnd为空！",L"GDMPLab",L"2024-03-30");

            QWidget* pMainWindowWidget = QWidget::find((WId)pAppWnd->GetApplicationWindowId());
            //DBG_WARN_AND_RETURN_VOID_UNLESS(pMainWindowWidget, L"pMainWindowWidget为空！", L"GDMPLab", L"2024-03-30");
            if (nullptr == pMainWindowWidget) {
                // The Application might want to do some jobs (see the "RunDataAssuranceServiceEndOfJournal"
                // debug mode). In the case,
                // the Application Window shouldn't be needed. Hence, use FYI, instead of DBG_WARN.
                FYI(L"Fails to get the Application Window Widget, through the winId!");
                return;
            }

            m_notificationHistoryPanel = nullptr;

            if (!m_notificationBar)
            {
                m_notificationBar = NEW_AS_QT_CHILD_QWIDGET(QtNotificationBar, pMainWindowWidget);
                if (nullptr == m_notificationBar) {
                    FYI(L"[ Warning ] The QtNotificationBar object cannot be created, in a child Qt thread!");
                }
                else {
                    m_notificationBar->show();
                }
            }
            if(!m_notifications[0] || !m_notifications[1] || !m_notifications[2])
            {
                QtAutoPtr<QtNotificationMessageBox> apQtNewNotification(
                    NEW_AS_QT_CHILD_QWIDGET(QtNotificationMessageBox, pNotification, pMainWindowWidget));
                if (nullptr == apQtNewNotification) {
                    std::wostringstream woss;
                    woss << L"[ Warning ] The QtNotificationMessageBox object for \""
                        << pNotification->GetText() 
                        << L"\" cannot be created, in a child Qt thread!";
                    FYI(woss.str());
                }
                else {
                    m_notifications[0] = TransferOwnership(m_notifications[1]);
                    m_notifications[1] = TransferOwnership(m_notifications[2]);
                    m_notifications[2] = TransferOwnership(apQtNewNotification);
                }
            }
            else
            {
                m_notifications[0]->SetNotification(m_notifications[1]->GetNotification());
                m_notifications[1]->SetNotification(m_notifications[2]->GetNotification());
                m_notifications[2]->SetNotification(pNotification);
            }

            if (m_notifications[0])
            {
                m_notifications[0]->setWindowOpacity(s_opacity0);
                m_notifications[0]->show();
            }

            if (m_notifications[1])
            {
                m_notifications[1]->setWindowOpacity(s_opacity1);
                m_notifications[1]->show();

            }

            if (m_notifications[2])
            {
                m_notifications[2]->setWindowOpacity(s_opacity2);
                m_notifications[2]->show();
            }

            MoveNotification();
        }

        // pos - MDIArea的右下脚
        void MoveNotification()
        {
            // Note: 调到此处时，m_pParentWidget为null是可能的，取决于外部是否提前调用SetNotificationParent，比如这个例子：
            // https://pm.glodon.com/newjira/browse/GBMP-19720 协同插件打开工作单元时，在成功打开后，才设置Parent，但在过程中提前发送了Notfication。
            if (nullptr == m_pParentWidget) return;

            IUiManager* pUiMgr = IUiManager::Get();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pUiMgr, L"pUiMgr为空！",L"GDMPLab",L"2024-03-30");
            IApplicationWindow* pAppWnd = pUiMgr->GetApplicationWindow();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pAppWnd, L"pAppWnd为空！",L"GDMPLab",L"2024-03-30");

            QWidget* pMainWindowWidget = QWidget::find((WId)pAppWnd->GetApplicationWindowId());
            DBG_WARN_AND_RETURN_VOID_UNLESS(pMainWindowWidget, L"pMainWindowWidget为空！",L"GDMPLab",L"2024-03-30");

            QPoint pnt = pMainWindowWidget->mapToGlobal(m_pParentWidget->pos());
            int x = pnt.x();
            int y = pnt.y();
            int width = m_pParentWidget->width();
            int height = m_pParentWidget->height();

            x += width;
            y += height;

            if (m_notificationBar)
            {
                QSize size = m_notificationBar->size();
                x = x - size.width() - s_gap;
                m_notificationBar->move(x, y - size.height() - s_gap);
            }

            x = x - s_gap;
            if (m_notificationHistoryPanel)
            {
                QSize size = m_notificationHistoryPanel->size();
                y = y - size.height() - s_gap;
                m_notificationHistoryPanel->move(QPoint(x - size.width(), y));
            }
            else
            {
                if (m_notifications[2])
                {
                    QSize size = m_notifications[2]->size();
                    y = y - size.height() - s_gap;
                    m_notifications[2]->move(QPoint(x - size.width(), y));
                }

                if (m_notifications[1])
                {
                    QSize size = m_notifications[1]->size();
                    y = y - size.height() - s_gap;
                    m_notifications[1]->move(QPoint(x - size.width(), y));
                }

                if (m_notifications[0])
                {
                    QSize size = m_notifications[0]->size();
                    y = y - size.height() - s_gap;
                    m_notifications[0]->move(QPoint(x - size.width(), y));
                }
            }
        }

        void CloseNotification()
        {
            FOR_EACH(notification,m_notifications)
            {
                if (QtNotificationMessageBox * pQtNtf = notification.releaseOwnership())
                    pQtNtf->deleteLater(); // It will get deleted in the main Qt thread.
                                           // Thus, this's Thread-Safe, we think.
            }

            if (m_notificationHistoryPanel != nullptr)
            {
                 m_notificationHistoryPanel->hide();
            }

        }

        void ShowHistory()
        {
            IUiManager* pUiMgr = IUiManager::Get();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pUiMgr, L"pUiMgr为空！",L"GDMPLab",L"2024-03-30");
            IApplicationWindow* pAppWnd = pUiMgr->GetApplicationWindow();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pAppWnd, L"pAppWnd为空！",L"GDMPLab",L"2024-03-30");

            QWidget* pMainWindowWidget = QWidget::find((WId)pAppWnd->GetApplicationWindowId());
            DBG_WARN_AND_RETURN_VOID_UNLESS(pMainWindowWidget, L"pMainWindowWidget为空！",L"GDMPLab",L"2024-03-30");

            m_notifications[0] = nullptr;
            m_notifications[1] = nullptr;
            m_notifications[2] = nullptr;

            if (!m_notificationHistoryPanel)
            {
                m_notificationHistoryPanel = NEW_AS_QT_CHILD_QWIDGET(QtNotificationHistoryPanel, pMainWindowWidget);
                if (nullptr == m_notificationHistoryPanel) {
                    FYI(L"[ Warning ] The QtNotificationHistoryPanel object cannot be created, in a child Qt thread!");
                    return;
                }
            }
           
            m_notificationHistoryPanel->BuildUi();
            m_notificationHistoryPanel->show();
            m_notificationHistoryPanel->setFocus();
            m_notificationHistoryPanel->activateWindow();

            MoveNotification();
        }

        bool SetNotificationParent(UInt64 parentId)
        {
            QWidget* pParentWidget = QWidget::find((WId)parentId);
            // 如果原来存在父窗口，移除父窗口事件过滤；
            if (m_pParentWidget != nullptr && m_pParentWidget != pParentWidget)
            {
                m_pParentWidget->removeEventFilter(this);
            }
            m_pParentWidget = pParentWidget;
            if (m_pParentWidget)
            { 
                m_pParentWidget->installEventFilter(this);
                // 为什么主窗口Move不引起MdiArea的移动事件 ？？？？
                IUiManager* pUiMgr = IUiManager::Get();
                DBG_WARN_AND_RETURN_FALSE_UNLESS(pUiMgr, L"pUiMgr为空！",L"GDMPLab",L"2024-03-30");
                IApplicationWindow* pAppWnd = pUiMgr->GetApplicationWindow();
                DBG_WARN_AND_RETURN_FALSE_UNLESS(pAppWnd, L"pAppWnd为空！",L"GDMPLab",L"2024-03-30");

                QWidget* pMainWindowWidget = QWidget::find((WId)pAppWnd->GetApplicationWindowId());
                DBG_WARN_AND_RETURN_FALSE_UNLESS(pMainWindowWidget, L"pMainWindowWidget为空！",L"GDMPLab",L"2024-03-30");
                pMainWindowWidget->installEventFilter(this);
                return true;
            }
            return false;
        }

    private:
        QPointer<QWidget>  m_pParentWidget;
        QtAutoPtr<QtNotificationMessageBox> m_notifications[3];
        QtAutoPtr<QtNotificationBar> m_notificationBar;
        QtAutoPtr<QtNotificationHistoryPanel> m_notificationHistoryPanel;
    };
}


gcmp::UiNotificationManager::UiNotificationManager()
{
    m_oImpl = NEW_AS_OWNER_PTR(UiNotificationManagerImpl);
}

gcmp::UiNotificationManager::~UiNotificationManager()
{

}

gcmp::UiNotificationManager* gcmp::UiNotificationManager::Get()
{
    return s_uiNotificationManagerCreator.Get();
}

void gcmp::UiNotificationManager::MoveNotification()
{
    m_oImpl->MoveNotification();
}

void gcmp::UiNotificationManager::CloseNotification()
{
    m_oImpl->CloseNotification();
}

void gcmp::UiNotificationManager::ShowHistory()
{
    m_oImpl->ShowHistory();
}

bool gcmp::UiNotificationManager::SetNotificationParent(UInt64 parentId)
{
    return m_oImpl->SetNotificationParent(parentId);
}
